package init.auto

import java.util.HashMap;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.FileSystemOperations;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.*;

import javax.inject.Inject;

import org.apache.groovy.json.internal.*
import groovy.json.*
import groovy.xml.*
import static groovy.io.FileType.*
import java.util.regex.*
import org.gradle.util.internal.GFileUtils

import org.gradle.api.artifacts.FileCollectionDependency
import init.auto.JdepsEntry

// https://docs.gradle.org/current/javadoc/org/gradle/api/DefaultTask.html
abstract class AutoScanDependencyTask extends DefaultTask {

    @Input
    abstract Property<org.gradle.api.Project> getProject1()
    @Input
    abstract Property<String> getCalchomepath1()
    @Input
    abstract Property<String> getScanModuleDir()

    @Internal
    def scanModuleName
    @Internal
    def domainName
    @Internal
    def scanModulePath
    @Internal
    def scanModuleBuildFile
    @Internal
    def commandSuffix
    @Internal
    def commandNccmainSuffix
    @Internal
    def existModules = []
    @Internal
    def consoleEncoding = "utf-8"
    @Internal
    def virtualModules = ['nccweb', 'nccmain']
    // 对模块jar的依赖
    @Internal
    def dependenciesCollection = []
    // 对模块jar的依赖的模块名
    @Internal
    def dependenciesNameCollection = []
    // 对模块classes的依赖，主要是public
    @Internal
    def dependenciesClassesNameCollection = []

    // 超过3个依赖则选择全部依赖【*】
    @Internal
    def dependenciesLimit = 3
    @Internal
    def isOutputJdepsFiles = false
    @Internal
    String beginColor = "\033[38;2;41;184;219m"
    @Internal
    String endColor = "\033[0m"
    @Internal
    String prefixColor = "\033[38;2;41;184;219m * \033[0m"


    @TaskAction
    def init() {
        println "${beginColor}Scan dependencies ${scanModuleDir.get()} from [${calchomepath1.get()}] ${endColor}"

        // 领域前缀
        domainName = scanModuleDir.get().replaceAll('_\\w+','').toLowerCase()
        // 扫描模块全路径
        scanModulePath = project1.get().file("${project1.get().rootProject.rootDir}/subprojects/${scanModuleDir.get()}")
        scanModuleBuildFile = project1.get().file("${scanModulePath}/build.gradle")

        // 扫描模块别名
        scanModuleName = scanModuleDir.get().replaceAll('\\w+_','').toLowerCase()

        def moduleXml = project1.get().file("${scanModulePath}/META-INF/module.xml")
        if (moduleXml.exists()) {
            def module = new groovy.xml.XmlSlurper().parse(moduleXml)
            scanModuleName = module.@name.text().toLowerCase()
        }


        // 1.扫描home下模块的private。目录下有jar，结尾必须加*
        commandSuffix = "modules/${scanModuleName}/META-INF/lib"
        // 1.2 扫描home下模块的public
        commandNccmainSuffix = "modules/${scanModuleName}/lib"
        // 无需扫描的模块 -- 全局公共依赖 -- bip.submodule.gradle
        existModules = [scanModuleName, 'baseapp', 'riart', 'uapbd', 'uapbs', 'uapfw', 'iuap', 'lightsmart', 'lcdp']

    }

    AutoScanDependencyTask() {
        setGroup("init auto base")
        setDescription("扫描目錄的依赖树并生成: build.gradle")

        // 删除eclipse配置文件
        doLast {
            project1.get().delete project1.get().file("${scanModulePath}/.settings")
            project1.get().delete project1.get().file("${scanModulePath}/.classpath")
            project1.get().delete project1.get().file("${scanModulePath}/.project")
        }


        // 开始扫描模块private
        doLast {
            println "${prefixColor}Begin scan ${scanModuleName} private Dependencies ..."

            def output = JdepsEntry.run(calchomepath1.get() as String, scanModuleName as String, commandSuffix as String);
            if (isOutputJdepsFiles) {
                project1.get().file("./jdeps_${scanModuleName}_private.txt").text = output
            }

            collectDependenciesFromJdeps(output, dependenciesNameCollection, dependenciesCollection, dependenciesClassesNameCollection, existModules, "private")
        }

        // 开始扫描模块private/classes
        doLast {
            println "${prefixColor}Begin scan ${scanModuleName} private/classes Dependencies ..."
            if (project1.get().file("${calchomepath1.get()}/modules/${scanModuleName}/META-INF/classes").exists()) {
                // 1.扫描home下模块的private。目录下有jar，结尾必须加*
                def output = JdepsEntry.run(calchomepath1.get() as String, scanModuleName as String, "modules/${scanModuleName}/META-INF/classes" as String);
                if (isOutputJdepsFiles) {
                    project1.get().file("./jdeps_${scanModuleName}_private_classes.txt").text = output
                }

                collectDependenciesFromJdeps(output, dependenciesNameCollection, dependenciesCollection, dependenciesClassesNameCollection, existModules, "private")
            } 
        }

        doLast {
            // 依赖名
            def fullText = 
                """
                |plugins {
                |    id 'bip.submodule'
                |}
                |
                |
                |""".stripMargin()
            dependenciesNameCollection.each {
                fullText += "def ${renameKey(it)} = modules + '/${it}/lib'\n"
            }

            dependenciesClassesNameCollection.removeAll(dependenciesNameCollection)
            dependenciesClassesNameCollection.each {
                fullText += "def ${renameKey(it)} = modules + '/${it}/lib'\n"
            }

            // 依赖路径
            fullText += 
                """
                |
                |dependencies {
                |""".stripMargin()
                
                // 默认依赖nccmain，所有模块的public
                fullText +=
                """
                |    implementation project(':nccmain')
                |    // evaluate expression
                |    runtimeOnly project(':nccweb')
                |
                |""".stripMargin()

            fullText += generateDependenciesDefined(dependenciesCollection, dependenciesNameCollection, dependenciesClassesNameCollection, dependenciesLimit)

            fullText +=
                """
                |}
                |""".stripMargin()

            GFileUtils.writeFile(fullText, scanModuleBuildFile, consoleEncoding)
            println "${prefixColor}查看输出文件: ${scanModuleBuildFile}"
        }
        // ##### private dependencies end ##### 


        doLast {
            // 公共部分的依赖，由动态组合构建删除
            existModules = existModules - scanModuleName
        }



        // 开始搜集nccweb/build.gradle下的现有的依赖项
        doLast {
            println "${prefixColor}Clean nccweb Dependencies collection ..."
            dependenciesNameCollection.clear()
            dependenciesCollection.clear()
            dependenciesClassesNameCollection.clear()

            project1.get().project(":nccweb").configurations.implementation.allDependencies.findAll {
                if (it instanceof FileCollectionDependency) {
                    it.resolve().each {
                        if (it.path.contains("modules")) {
                            def dependencyRelativePath = it.path.split("modules")[1].replace(File.separator, "/").replaceFirst("/", "")
                            def moduleName = dependencyRelativePath.split("/")[0]
                            // 排除公共依赖项
                            if (!existModules.contains(moduleName)) {
                                if (dependencyRelativePath.endsWith('classes')) {
                                    dependenciesClassesNameCollection.add(moduleName)
                                } else {
                                    dependenciesNameCollection.add(moduleName)
                                    dependenciesCollection.add(dependencyRelativePath)
                                }
                            }
                        }
                    }
                }
            }
        }

        // 开始扫描模块nccweb
        doLast {
            println "${prefixColor}Begin scan ${scanModuleName} nccweb Dependencies ..."


            // 查找jar
            def filenames = []
            def p = Pattern.compile(".+ui${scanModuleName}_nccloud(_rt)?(Level-\\d)?(_${domainName})?\\.jar", Pattern.CASE_INSENSITIVE|Pattern.COMMENTS)
            project1.get().file("${calchomepath1.get()}/external/lib").traverse(type: FILES, maxDepth: 0) {
                Matcher m = p.matcher(it.path);
                if (m.matches()){
                    filenames.add( it.path )
                }
            }


            def output = " 文件[external/lib/ui${scanModuleName}_nccloud*.jar]不存在!"
            if (filenames.size > 0) {
                output = JdepsEntry.run(calchomepath1.get() as String, scanModuleName as String, filenames as List);
            } else {
                println output
            }
            if (isOutputJdepsFiles) {
                project1.get().file("./jdeps_${scanModuleName}_nccweb.txt").text = output
            }

            collectDependenciesFromJdeps(output, dependenciesNameCollection, dependenciesCollection, dependenciesClassesNameCollection, existModules, "nccweb")
        }

        // 开始扫描模块nccweb/classes
        doLast {
            println "${prefixColor}Begin scan ${scanModuleName} nccweb/classes Dependencies ..."

            def output = " 文件external/classes不存在!"
            if (project1.get().file("${calchomepath1.get()}/external/classes").exists()) {
                // 1.3 扫描home下模块的nccloud
                output = JdepsEntry.run(calchomepath1.get() as String, scanModuleName as String, "external/classes" as String);
            } else {
                println output
            }
            if (isOutputJdepsFiles) {
                project1.get().file("./jdeps_${scanModuleName}_nccweb_classes.txt").text = output
            }

            collectDependenciesFromJdeps(output, dependenciesNameCollection, dependenciesCollection, dependenciesClassesNameCollection, existModules, "nccweb")
        }


        doLast {
            println "${prefixColor}Begin generate nccweb/build.gradle ..."


            // 依赖名
            def fullText = 
                """
                |
                |plugins {
                |    id 'bip.submodule.virtual'
                |}
                |
                |description = "编译所有子模块下的 nccloud*/client/**/*.java"
                |
                |""".stripMargin()
            dependenciesNameCollection.each {
                fullText += "def ${renameKey(it)} = modules + '/${it}/lib'\n"
            }

            dependenciesClassesNameCollection.removeAll(dependenciesNameCollection)
            dependenciesClassesNameCollection.each {
                fullText += "def ${renameKey(it)} = modules + '/${it}/lib'\n"
            }


            // 依赖路径
            fullText += 
                """
                |dependencies {
                |    // client 依赖 public
                |""".stripMargin()
            // 默认依赖nccmain
            fullText += "    implementation project(':nccmain')\n\n"
            fullText += "    implementation fileTree(dir: external, includes: ['ui*.jar', 'pub*.jar'])\n\n"

            fullText += generateDependenciesDefined(dependenciesCollection, dependenciesNameCollection, dependenciesClassesNameCollection, dependenciesLimit)

            fullText +=
                """
                |}
                |""".stripMargin()

            def nccwebBuildFile = project1.get().project(":nccweb").buildFile
            GFileUtils.writeFile(fullText, nccwebBuildFile, consoleEncoding)
            println "${prefixColor}查看输出文件: ${nccwebBuildFile}"
        }
        // ##### nccweb dependencies end ##### 


        // 开始搜集nccmain/build.gradle下的现有的依赖项
        doLast {
            println "${prefixColor}Clean nccmain Dependencies collection ..."
            dependenciesNameCollection.clear()
            dependenciesCollection.clear()
            dependenciesClassesNameCollection.clear()


            project1.get().project(":nccmain").configurations.implementation.allDependencies.findAll {
                if (it instanceof FileCollectionDependency) {
                    it.resolve().each {
                        if (it.path.contains("modules")) {
                            def dependencyRelativePath = it.path.split("modules")[1].replace(File.separator, "/").replaceFirst("/", "")
                            def moduleName = dependencyRelativePath.split("/")[0]
                            // 排除公共依赖项
                            if (!existModules.contains(moduleName)) {
                                if (dependencyRelativePath.endsWith('classes')) {
                                    dependenciesClassesNameCollection.add(moduleName)
                                } else {
                                    dependenciesNameCollection.add(moduleName)
                                    dependenciesCollection.add(dependencyRelativePath)
                                }
                            }
                        }
                    }
                }
            }
        }

        // 开始扫描模块nccmain
        doLast {
            println "${prefixColor}Begin scan ${scanModuleName} nccmain Dependencies  ..."

            def output = JdepsEntry.run(calchomepath1.get() as String, scanModuleName as String, commandNccmainSuffix as String);
            if (isOutputJdepsFiles) {
                project1.get().file("./jdeps_${scanModuleName}_nccmain.txt").text = output
            }

            collectDependenciesFromJdeps(output, dependenciesNameCollection, dependenciesCollection, dependenciesClassesNameCollection, existModules, "nccmain")
        }

        // 开始扫描模块nccmain/classes
        doLast {
            println "${prefixColor}Begin scan ${scanModuleName} nccmain/classes Dependencies  ..."

            if (project1.get().file("${calchomepath1.get()}/modules/${scanModuleName}/classes").exists()) {
                // 1.2 扫描home下模块的public
                def output = JdepsEntry.run(calchomepath1.get() as String, scanModuleName as String, "modules/${scanModuleName}/classes" as String);
                if (isOutputJdepsFiles) {
                    project1.get().file("./jdeps_${scanModuleName}_nccmain_classes.txt").text = output
                }

                collectDependenciesFromJdeps(output, dependenciesNameCollection, dependenciesCollection, dependenciesClassesNameCollection, existModules, "nccmain")
            }
        }

        doLast {
            println "${prefixColor}Begin generate nccmain/build.gradle ..."


            // 依赖名
            def fullText = 
                """
                |
                |plugins {
                |    id 'bip.submodule.virtual'
                |}
                |
                |description = "编译所有子模块下的 public/**/*.java"
                |
                |""".stripMargin()
            dependenciesNameCollection.each {
                fullText += "def ${renameKey(it)} = modules + '/${it}/lib'\n"
            }

            dependenciesClassesNameCollection.removeAll(dependenciesNameCollection)
            dependenciesClassesNameCollection.each {
                fullText += "def ${renameKey(it)} = modules + '/${it}/lib'\n"
            }


            // 依赖路径
            fullText += 
                """
                |dependencies {
                |""".stripMargin()

            fullText += generateDependenciesDefined(dependenciesCollection, dependenciesNameCollection, dependenciesClassesNameCollection, dependenciesLimit)

            fullText +=
                """
                |}
                |""".stripMargin()

            def nccmainBuildFile = project1.get().project(":nccmain").buildFile
            GFileUtils.writeFile(fullText, nccmainBuildFile, consoleEncoding)
            println "${prefixColor}查看输出文件: ${nccmainBuildFile}"
        }
    }
        // ##### nccmain dependencies end ##### 
        // ##### virtual dependencies end ##### 


    // ====================== method area ======================
    // 收集依赖包
    def collectDependenciesFromJdeps(output, dependenciesNameCollection, dependenciesCollection, dependenciesClassesNameCollection, existModules, module1) {
        output.split('\n').each {
            // 排除警告格式
            if (it.contains('警告:')  || it.contains('找不到') || !it.contains('->') || !it.contains('modules')) {
                return;
            }

            def dependencyFile = it.split('modules')[1].strip().replace(File.separator,'/').replaceFirst('/', '')
            def dependencyModuleName = dependencyFile.split('/')[0]

            if (dependencyFile.contains('/')) {
                if (dependencyModuleName in existModules) {
                    return
                }
                // for classes
                if (dependencyFile.endsWith('classes')) {
                    dependenciesClassesNameCollection.add(dependencyModuleName)
                } else {
                    // for jar
                    dependenciesNameCollection.add(dependencyModuleName)
                    dependenciesCollection.add(dependencyFile)
                }
            } 
        }

        // 追加依賴 - 全局緩存
        if (module1 in virtualModules) {
            // 缓存Key
            def dependenciesNameCollectionKey = "dependenciesNameCollection_${module1}"
            def dependenciesCollectionKey = "dependenciesCollection_${module1}"
            def dependenciesClassesNameCollectionKey = "dependenciesClassesNameCollection_${module1}"

            // 取缓存
            dependenciesNameCollection.addAll(project1.get().ext[dependenciesNameCollectionKey])
            dependenciesCollection.addAll(project1.get().ext[dependenciesCollectionKey])
            dependenciesClassesNameCollection.addAll(project1.get().ext[dependenciesClassesNameCollectionKey])
        }

        // 当前扫描依赖集合
        dependenciesNameCollection = dependenciesNameCollection.unique().sort()
        dependenciesCollection = dependenciesCollection.unique().sort()
        dependenciesClassesNameCollection = dependenciesClassesNameCollection.unique().sort()

        // 更新依賴 - 全局緩存
        if (module1 in virtualModules) {
            // 缓存Key
            def dependenciesNameCollectionKey = "dependenciesNameCollection_${module1}"
            def dependenciesCollectionKey = "dependenciesCollection_${module1}"
            def dependenciesClassesNameCollectionKey = "dependenciesClassesNameCollection_${module1}"

            // 覆盖缓存
            project1.get().ext[dependenciesNameCollectionKey].clear()
            project1.get().ext[dependenciesNameCollectionKey].addAll(dependenciesNameCollection)
            project1.get().ext[dependenciesCollectionKey].clear()
            project1.get().ext[dependenciesCollectionKey].addAll(dependenciesCollection)
            project1.get().ext[dependenciesClassesNameCollectionKey].clear()
            project1.get().ext[dependenciesClassesNameCollectionKey].addAll(dependenciesClassesNameCollection)
        }
    }

    // 生成依赖定义【輕量端】 public 
    def generateDependenciesDefined(dependenciesCollection, dependenciesNameCollection, dependenciesClassesNameCollection, dependenciesLimit) {
        def fullText = ""
        def dependencyModuleName = ''
        def libJars = []
        def metaJars = []

        /**
        * 1. 先循环有依赖jar
        * 2. 再处理剩余有依赖jar
        * 3. 最后处理仅依赖classes
        */
        dependenciesCollection.each {
            def sp = it.split('/')
            def dirName = sp[0]
            def jarName = sp[2]
            
            if (dependencyModuleName == '') {
                dependencyModuleName = dirName
            }

            if (dependencyModuleName == dirName) {
                libJars.add(jarName)
                if (jarName.startsWith('pub')) {
                    metaJars.add(jarName - 'pub')
                }
            } else {
                // 一、循环内部
                if (libJars.size > 0) {
                    // println dependencyModuleName + ' -> ' + dirName

                    if (libJars.size <= dependenciesLimit) {
                        // add public/classes
                        fullText += "    implementation files(${renameKey(dependencyModuleName)}.replace(lib, classes))\n"
                        fullText += "    implementation fileTree(dir: ${renameKey(dependencyModuleName)}, includes: ${ libJars.collect{"'${it}'"} })\n"
                        if (metaJars.size > 0) {
                            // add private/classes
                            fullText += "    runtimeOnly files(${renameKey(dependencyModuleName)}.replace(lib, metaLib).replace(lib, classes))\n"
                            fullText += "    runtimeOnly fileTree(dir: ${renameKey(dependencyModuleName)}.replace(lib, metaLib), includes: ${ metaJars.collect{"'${it}'"} })\n\n"
                        } else {
                            fullText += "\n"
                        }
                    } else {
                        // add public/classes
                        fullText += "    implementation files(${renameKey(dependencyModuleName)}.replace(lib, classes))\n"
                        fullText += "    implementation fileTree(dir: ${renameKey(dependencyModuleName)}, includes: ['*.jar'], excludes: ['*_src.jar', '*-sources.jar'])\n"
                        // add private/classes
                        fullText += "    runtimeOnly files(${renameKey(dependencyModuleName)}.replace(lib, metaLib).replace(lib, classes))\n"
                        fullText += "    runtimeOnly fileTree(dir: ${renameKey(dependencyModuleName)}.replace(lib, metaLib), includes: ['*.jar'], excludes: ['*_src.jar', '*-sources.jar'])\n\n"
                    }
                }

                // 2. clear
                dependencyModuleName = dirName 
                libJars = []
                metaJars = []

                // 3. append
                libJars.add(jarName)
                if (jarName.startsWith('pub')) {
                    metaJars.add(jarName - 'pub')
                }
            }
        }

        // 二、剩余部分
        if (libJars.size > 0) {

            if (libJars.size <= dependenciesLimit) {
                // add public/classes
                fullText += "    implementation files(${renameKey(dependencyModuleName)}.replace(lib, classes))\n"
                fullText += "    implementation fileTree(dir: ${renameKey(dependencyModuleName)}, includes: ${ libJars.collect{"'${it}'"} })\n"
                if (metaJars.size > 0) {
                    // add private/classes
                    fullText += "    runtimeOnly files(${renameKey(dependencyModuleName)}.replace(lib, metaLib).replace(lib, classes))\n"
                    fullText += "    runtimeOnly fileTree(dir: ${renameKey(dependencyModuleName)}.replace(lib, metaLib), includes: ${ metaJars.collect{"'${it}'"} })\n\n"
                } else {
                    fullText += "\n"
                }
            } else {
                // add public/classes
                fullText += "    implementation files(${renameKey(dependencyModuleName)}.replace(lib, classes))\n"
                fullText += "    implementation fileTree(dir: ${renameKey(dependencyModuleName)}, includes: ['*.jar'], excludes: ['*_src.jar', '*-sources.jar'])\n"
                // add private/classes
                fullText += "    runtimeOnly files(${renameKey(dependencyModuleName)}.replace(lib, metaLib).replace(lib, classes))\n"
                fullText += "    runtimeOnly fileTree(dir: ${renameKey(dependencyModuleName)}.replace(lib, metaLib), includes: ['*.jar'], excludes: ['*_src.jar', '*-sources.jar'])\n\n"
            }
        }

        // 三、*/classes
        if (dependenciesClassesNameCollection.size > 0) {
            dependenciesClassesNameCollection.each {
                // add public/classes
                fullText += "    implementation files(${renameKey(it)}.replace(lib, classes))\n\n"
                // add private/classes
                fullText += "    implementation files(${renameKey(it)}.replace(lib, metaLib).replace(lib, classes))\n\n"
            }
        }

        println "${prefixColor}生成依賴個數：${dependenciesNameCollection.size + dependenciesClassesNameCollection.size}"

        return fullText
    }


    // 模块名与gradle关键字相同时，重命名模块名
    def renameKey(moduleName) {
        return "m_" + moduleName
    }
    // end method area
}